home *** CD-ROM | disk | FTP | other *** search
/ SGI Developer Toolbox 6.1 / SGI Developer Toolbox 6.1 - Disc 1.iso / toolbox / src / exampleCode / opengl / utilities / isfast / README < prev   
Text File  |  1996-11-11  |  17KB  |  475 lines

  1. ______________________________________________________________________
  2. The Problem
  3.  
  4. When you're writing an OpenGL application, how do you know whether a
  5. particular feature (like depth buffering or texture mapping) is fast
  6. enough to be useful?
  7.  
  8. If you want your application to run fast on a variety of machines,
  9. while taking advantage of as many hardware features as possible, you
  10. need to write code that makes configuration decisions at runtime.
  11.  
  12. For OpenGL's predecessor, IRIS GL, you could call getgdesc() to
  13. determine whether a feature had hardware support.  For example, you
  14. could determine whether a Z buffer existed.  If it did, you might
  15. assume that Z buffering was fast, and therefore your application would
  16. use it.  Usually (but not always) this was a reasonable approach.
  17.  
  18. In OpenGL, things are more complicated.  All the core features are
  19. provided, even when there is no hardware support for them and they must
  20. be implemented completely in software.  There is no OpenGL routine that
  21. reports whether a feature is implemented partially or completely in
  22. hardware.
  23.  
  24. Furthermore, features interact in essentially unpredictable ways.  For
  25. example, a machine might have hardware support for depth buffering, but
  26. only for some comparison functions.  Or depth buffering might be fast
  27. only as long as stencilling is not enabled.  Or depth buffering might
  28. be fast when drawing to a window, but slow when drawing to a pixmap.
  29. And so on.  A routine that identifies hardware support for particular
  30. features is actually a lot more complicated and less useful than you'd
  31. like!
  32.  
  33.  
  34.  
  35. ______________________________________________________________________
  36. A Solution
  37.  
  38. So how do you decide whether a given OpenGL feature is fast?  The
  39. answer is "Measure it." Since the performance of a section of graphics
  40. code is dependent on dozens of pieces of information from the runtime
  41. environment, no other method is as well-defined and reliable.
  42.  
  43. Performance measurement can be tricky.  You need to handle the cases
  44. when you're displaying over a network, as well as locally.  You also
  45. want to think about flushing the graphics pipeline properly, and
  46. accounting for the resulting overhead.
  47.  
  48. Measuring all the features needed by your application might take a
  49. while -- probably too long to make your users wait for the results each
  50. time the application starts.  Therefore you'll want to save performance
  51. measurements and reuse them whenever possible.
  52.  
  53. And you might want to measure things other than graphics:  Disk and
  54. network throughput, processing time for a particular set of data,
  55. performance on uniprocessor and multiprocessor systems.
  56.  
  57. This document describes two libraries that can help with all of the
  58. tasks just mentioned.
  59.  
  60.     libpdb
  61.         "Performance DataBase" routines for measuring execution
  62.         rates and maintaining a simple database.
  63.  
  64.     libisfast
  65.         A set of routines demonstrating libpdb that answer
  66.         common questions about the performance of OpenGL
  67.         features (using reasonable but subjective criteria).
  68.  
  69. These libraries can't substitute for comprehensive benchmarking and
  70. performance analysis, and don't replace more sophisticated tools (like
  71. IRIS Performer and IRIS Inventor) that optimize application performance
  72. in a variety of ways.  However, they can handle simple tasks easily.
  73.  
  74.  
  75.  
  76. ______________________________________________________________________
  77. libpdb Tutorial
  78.  
  79. libpdb provides five routines:
  80.  
  81.     pdbOpen() opens the performance database.
  82.  
  83.     pdbReadRate() reads the execution rate for a given benchmark
  84.     (identified by a machine name, application name, benchmark
  85.     name, and version string) from the database.
  86.  
  87.     pdbMeasureRate() measures the execution rate for a given
  88.     operation.
  89.  
  90.     pdbWriteRate() writes the execution rate for a given benchmark
  91.     into the database.
  92.  
  93.     pdbClose() closes the performance database and writes it back
  94.     to disk if necessary.
  95.  
  96. All libpdb routines return a value of type pdbStatusT, which is a
  97. bitmask of error conditions.  If the value is zero (PDB_NO_ERROR), then
  98. the call completed successfully.  If the value is nonzero, then it is a
  99. combination of one or more of the following conditions:
  100.  
  101.     PDB_OUT_OF_MEMORY       An attempt to allocate memory failed.
  102.  
  103.     PDB_SYNTAX_ERROR        The database contains one or more
  104.                 records that could not be parsed.
  105.  
  106.     PDB_NOT_FOUND           The database does not contain the
  107.                 record requested by the application.
  108.  
  109.     PDB_CANT_WRITE          The database file could not be
  110.                 updated.
  111.  
  112.     PDB_NOT_OPEN        pdbOpen() was not invoked before
  113.                 calling one of the other libpdb
  114.                 routines.
  115.  
  116.     PDB_ALREADY_OPEN    pdbOpen() was called while the database
  117.                 is still open (e.g., before pdbClose()
  118.                 is invoked).
  119.  
  120. Every program must call pdbOpen() before using the database, and
  121. pdbClose() when the database is no longer needed.  pdbOpen() opens the
  122. database file (stored in $HOME/.pdb2 on UNIX systems) and reads all the
  123. performance measurements into main memory.  pdbClose() releases all
  124. memory used by the library, and writes the database back to its file if
  125. any changes have been made by invoking pdbWriteRate().
  126.  
  127.     Synopsis
  128.  
  129.         pdbStatusT pdbOpen(void);
  130.  
  131.         pdbStatusT pdbClose(void);
  132.  
  133. pdbOpen() returns PDB_NO_ERROR on success, PDB_OUT_OF_MEMORY if there
  134. was insufficient main memory to store the entire database,
  135. PDB_SYNTAX_ERROR if the contents of the database could not be parsed or
  136. seemed implausible (e.g. a nonpositive performance measurement), or
  137. PDB_ALREADY_OPEN if the database has been opened by a previous call to
  138. pdbOpen() and not closed by a call to pdbClose().
  139.  
  140. pdbClose() returns PDB_NO_ERROR on success, PDB_CANT_WRITE if the
  141. database file is unwritable for any reason, or PDB_NOT_OPEN if the
  142. database is not open.
  143.  
  144. Normally applications should look for the performance data they need
  145. before going to the trouble of taking measurements.  pdbReadRate() is
  146. used for this.
  147.  
  148.     Synopsis
  149.  
  150.         pdbStatusT pdbReadRate (
  151.                 const char* machineName,
  152.                 const char* applicationName,
  153.                 const char* benchmarkName,
  154.                 const char* versionString,
  155.                 double* rate
  156.                 );
  157.     
  158.     Example
  159.  
  160.         main() {
  161.             double rate;
  162.             pdbOpen();
  163.             if (pdbReadRate(NULL, "myApp", "triangles",
  164.                glGetString(GL_VERSION), &rate)
  165.                   == PDB_NO_ERROR)
  166.                 printf("%g triangle calls per second\n", rate);
  167.             pdbClose();
  168.             }
  169.  
  170. The first argument is a zero-terminated string giving the name of the
  171. machine for which the measurement is sought.  If NULL, the default
  172. machine name is used.  (In X11 environments, the display name is an
  173. appropriate choice, and the default machine name is the content of the
  174. DISPLAY environment variable.)
  175.  
  176. The second argument is the name of the application.  This is used as an
  177. additional database key to reduce accidental collisions between
  178. benchmark names.
  179.  
  180. The third argument is the name of the benchmark.
  181.  
  182. The fourth argument is a string identifying the desired version of the
  183. benchmark.  For OpenGL performance measurements, the string returned
  184. by glGetString(GL_VERSION) is a good value for this argument.  Other
  185. applications might use the version number of the benchmark, rather
  186. than the version number of the system under test.
  187.  
  188. The fourth argument is a pointer to a double-precision floating-point
  189. variable which receives the performance measurement (the "rate") from
  190. the database.  The rate indicates the number of benchmark operations per
  191. second that were measured on a previous run.
  192.  
  193. if pdbReadRate() returns zero, then it completed successfully and the
  194. rate is returned in the last argument.  If the requested benchmark is
  195. not present in the database, it returns PDB_NOT_FOUND.  Finally, if
  196. pdbReadRate() is called when the database has not been opened by
  197. pdbOpen(), it returns PDB_NOT_OPEN.
  198.  
  199. When the application is run for the first time, or when the performance
  200. database file has been removed (perhaps to allow a fresh start after a
  201. hardware upgrade), pdbReadRate() will not be able to find the desired
  202. benchmark.  If this happens, the application should use
  203. pdbMeasureRate() to make a measurement.
  204.  
  205.     Synopsis
  206.  
  207.         pdbStatusT pdbMeasureRate (
  208.                 pdbCallbackT initialize,
  209.                 pdbCallbackT operation,
  210.                 pdbCallbackT finalize,
  211.                 int calibrate,
  212.                 double* rate
  213.                 );
  214.     
  215.     Example
  216.  
  217.         void SetupOpenGLState(void) {
  218.             /* set all OpenGL state to desired values */
  219.             }
  220.  
  221.         void DrawTriangles(void) {
  222.             glBegin(GL_TRIANGLE_STRIP);
  223.                 /* specify some vertices... */
  224.             glEnd();
  225.             }
  226.         
  227.         main() {
  228.             double rate;
  229.             pdbOpen();
  230.             if (pdbReadRate(NULL, "myApp", "triangles",
  231.                glGetString(GL_VERSION), &rate)
  232.                   != PDB_NO_ERROR) {
  233.                 SetupOpenGLState();
  234.                 pdbMeasureRate(glFinish, DrawTriangles,
  235.                     glFinish, 1, &rate);
  236.                 }
  237.             printf("%g triangle calls per second\n", rate);
  238.             pdbClose();
  239.             }
  240.  
  241. The first argument is a pointer to the initialization function.  The
  242. initialization function is run before each set of operations.  For
  243. OpenGL performance measurement, it's appropriate to use glFinish() for
  244. initialization, to make sure that the graphics pipe is quiet.  However,
  245. for other performance measurements, the initialization function could
  246. be used to create test data, preload caches, etc.  It may be NULL, in
  247. which case no initialization is performed.
  248.  
  249. The second argument is a pointer to the operation function.  This
  250. function performs the operations that are to be measured.  Usually
  251. you'll want to make sure that any global state needed by the operation
  252. is set up before calling the operation function, so that you don't
  253. include the cost of the setup operations in the measurement.
  254.  
  255. The third argument is a pointer to a finalization function.  This is
  256. run once, after all the calls to the operation function are complete.
  257. In the example above, we used glFinish() again to ensure that the
  258. graphics pipeline is idle.  It may be NULL, in which case no
  259. finalization is performed.
  260.  
  261. The finalization function must be "calibrated" so that the overhead of
  262. calling it may be subtracted from the time used by the operation
  263. function.  If the fourth argument is nonzero, then pdbMeasureRate()
  264. calibrates the finalization function.  If the fourth argument is zero,
  265. then pdbMeasureRate() uses the results of the previous calibration.
  266. Recalibrating each measurement is the safest approach, but it roughly
  267. doubles the amount of time needed for a measurement.  For OpenGL, it
  268. should be OK to calibrate once and recalibrate only when using a
  269. different X11 display.
  270.  
  271. The final argument is a pointer to a double-precision floating-point
  272. variable which receives the execution rate.  This rate is the number of
  273. times the operation function was called per second.
  274.  
  275. pdbMeasureRate() attempts to compute a number of repetitions that
  276. results in a run time of about one second.  (Calibration requires an
  277. additional second.)  It's reasonably careful about timekeeping on
  278. systems with low-resolution clocks.
  279.  
  280. pdbMeasureRate() always returns PDB_NO_ERROR.
  281.  
  282. Once a rate has been measured, it should be stored in the database
  283. by calling pdbWriteRate().
  284.  
  285.     Synopsis
  286.  
  287.         pdbStatusT pdbWriteRate (
  288.                 const char* machineName,
  289.                 const char* applicationName,
  290.                 const char* benchmarkName,
  291.                 const char* versionString,
  292.                 double rate
  293.                 );
  294.     
  295.     Example
  296.  
  297.         main() {
  298.             double rate;
  299.             pdbOpen();
  300.             if (pdbReadRate(NULL, "myApp", "triangles",
  301.                glGetString(GL_VERSION), &rate)
  302.                   != PDB_NO_ERROR) {
  303.                 SetupOpenGL();
  304.                 pdbMeasureRate(glFinish, DrawTriangles,
  305.                     glFinish, 1, &rate);
  306.                 pdbWriteRate(NULL, "myApp", "triangles",
  307.                     glGetString(GL_VERSION), rate);
  308.                 }
  309.             printf("%g triangle calls per second\n", rate);
  310.             pdbClose();
  311.             }
  312.  
  313. The first four arguments of pdbWriteRate() match the first four
  314. arguments of pdbReadRate().
  315.  
  316. The final argument is the performance measurement to be saved in the
  317. database.
  318.  
  319. pdbWriteRate() will return PDB_NO_ERROR if the performance measurement
  320. was added to the in-memory copy of the database, PDB_OUT_OF_MEMORY if
  321. there was insufficient main memory to do so, or PDB_NOT_OPEN if the
  322. database is not open.
  323.  
  324. When pdbWriteRate() is called, the in-memory copy of the performance
  325. database is marked "dirty."  pdbClose() takes note of this and writes
  326. the database back to disk.
  327.  
  328.  
  329.  
  330. ______________________________________________________________________
  331. libisfast Tutorial
  332.  
  333. libisfast is a set of demonstration routines that show how libpdb can
  334. be used to measure and maintain OpenGL performance data.  libisfast is
  335. based on purely subjective performance criteria.  If they're
  336. appropriate for your application, please feel free to use them.  If
  337. not, please copy the source code and modify it accordingly.
  338.  
  339. In all cases that follow, the term "triangles" refers to a triangle
  340. strip with 37 vertices.  The triangles are drawn with perspective
  341. projection, lighting, and smooth (Gouraud) shading.  Unless otherwise
  342. stated, display-list-mode drawing is used.  (This makes isfast yield
  343. more useful results when the target machine is being accessed over
  344. a network.)
  345.  
  346. The app must initialize isfast before performing any performance
  347. measurements, and clean up after the measurements are finished.  On X11
  348. systems these tasks are accomplished by calling
  349.  
  350.     int IsFastXOpenDisplay(const char* displayName);
  351.  
  352. and
  353.  
  354.     void IsFastXCloseDisplay(void);
  355.  
  356. respectively.  IsFastOpenXDisplay() returns zero if the named display
  357. could not be opened, and nonzero if the display was opened
  358. successfully.
  359.  
  360. DepthBufferingIsFast() returns nonzero if depth buffered triangles can
  361. be drawn at least one-half as fast as triangles without depth
  362. buffering:
  363.  
  364.     int DepthBufferingIsFast(void);
  365.  
  366. ImmediateModeIsFast() returns nonzero if immediate-mode triangles can
  367. be drawn at least one-half as fast as display-listed triangles:
  368.  
  369.     int ImmediateModeIsFast(void);
  370.  
  371. Note that one significant use of ImmediateModeIsFast() might be to
  372. decide whether a "local" or a "remote" rendering strategy is
  373. appropriate.  If immediate mode is fast, as on a local workstation, it
  374. might be best to use it and avoid the memory cost of duplicating the
  375. application's data structures in display lists.  If immediate mode is
  376. slow, as is likely for a remote workstation, it may be best to use
  377. display lists for bulky geometry and textures.
  378.  
  379. StencillingIsFast() returns nonzero if stencilled triangles can be
  380. drawn at least one-half as fast as non-stencilled triangles:
  381.  
  382.     int StencillingIsFast(void);
  383.  
  384. TextureMappingIsFast() returns nonzero if texture-mapped triangles can
  385. be drawn at least one-half as fast as non-texture-mapped triangles:
  386.  
  387.     int TextureMappingIsFast(void);
  388.  
  389. Although the routines in libisfast will be useful for a number of
  390. applications, we suggest that you study them and modify them for your
  391. own use.  That way you'll explore the particular performance
  392. characteristics of your machines:  their sensitivity to triangle size,
  393. triangle strip length, culling, stencil function, texture map type,
  394. texture coordinate generation method, etc.
  395.  
  396. Keep in mind that while the results of the libisfast routines are
  397. interesting, they apply to very limited special cases.  You should
  398. always consider using a more general tool like Inventor or Performer.
  399.  
  400.  
  401.  
  402. ______________________________________________________________________
  403. Notes
  404.  
  405. The source directory has three subdirectories:
  406.  
  407.     demo
  408.         Contains a trivial main program to call the routines
  409.         in libisfast.
  410.  
  411.     libisfast
  412.         Source code for libisfast.
  413.  
  414.     libpdb
  415.         Source code for libpdb.
  416.  
  417. Each subdirectory has its own makefile, and there is a master makefile
  418. in the main source directory.
  419.  
  420. This code has been tested lightly in IRIX 5.3, a UNIX SVR4 environment,
  421. on an Indigo2 Extreme and an Onyx RealityEngine.
  422.  
  423.  
  424.  
  425.  
  426. ______________________________________________________________________
  427. Revision History
  428.  
  429. Version 1.0:
  430.  
  431.     Initial release.
  432.  
  433. Version 2.0:
  434.     
  435.     libpdb:
  436.  
  437.         Added support for benchmark version identifiers.  This
  438.         solved problems with out-of-date libpdb databases, but
  439.         made it necessary to create a new pdb database format
  440.         and file.
  441.  
  442.         Added calibration option for pdbMeasureRate().  This
  443.         speeds up libisfast significantly.
  444.  
  445.         Allowed more special characters in the machine name,
  446.         application name, benchmark name, and version string. 
  447.         For example, these strings may now contain blanks and
  448.         tabs.  In the database file, special characters are
  449.         escaped with backslashes.
  450.  
  451.     libisfast:
  452.  
  453.         Removed dependency on the libtk toolkit.  This makes
  454.         it easier to integrate libisfast with real
  455.         applications, but exposes some system-specific
  456.         interfaces.  The separation between
  457.         window-system-independent code and
  458.         window-system-dependent code is not as thorough as it
  459.         used to be (I refuse to create *another* toolkit), so
  460.         porting this version of libisfast to OS/2 or Windows
  461.         will take a little more effort.
  462.  
  463.         Used visinfo package for selecting Visuals.
  464.  
  465.         Made calibration operations less frequent.
  466.  
  467.         Restructured tests to use display lists wherever
  468.         possible, in order to get more reasonable results for
  469.         machines that are accessed over the network.
  470.  
  471.     demo:
  472.  
  473.         Replaced old initialization and cleanup calls with new
  474.         ones.
  475.